import subprocess as sp
from pathlib import Path as P
import os

class AudioConverter:
    """音频转换工具的核心逻辑处理类"""
    
    # 支持的输入格式
    SUPPORTED_INPUT_FORMATS = {'.mp3', '.aac', '.wma', '.ogg', '.wav', '.flac', '.ape', '.alac', 
                              '.mp4', '.avi', '.mkv', '.flv', '.ts', '.mov'}
    
    # 支持的输出格式
    SUPPORTED_OUTPUT_FORMATS = {'.mp3', '.m4a'}
    
    def __init__(self):
        # 初始化质量映射
        self.q_map = {
            'mp3': {'高质量': '-q:a 0', '中等质量': '-q:a 2', '低质量': '-q:a 6', '源文件质量': 'source'},
            'm4a': {'高质量': '-b:a 256k', '中等质量': '-b:a 192k', '低质量': '-b:a 128k', '源文件质量': 'source'}
        }
        
        # 用于UI更新的回调函数
        self.progress_callback = None
        self.log_callback = None
    
    def set_callbacks(self, progress_callback, log_callback):
        """设置进度和日志回调函数"""
        self.progress_callback = progress_callback
        self.log_callback = log_callback
    
    def log(self, message):
        """记录日志，如果设置了回调则调用"""
        if self.log_callback:
            self.log_callback(message)
        else:
            print(message)
    
    def update_progress(self, value):
        """更新进度，如果设置了回调则调用"""
        if self.progress_callback:
            self.progress_callback(value)
        else:
            print(f"进度: {value}%")
    
    def is_supported_input(self, file_path):
        """检查文件是否为支持的输入格式"""
        suffix = P(file_path).suffix.lower()
        return suffix in self.SUPPORTED_INPUT_FORMATS
    
    def get_supported_input_formats_str(self):
        """获取支持的输入格式字符串，用于文件选择对话框"""
        return "音频/视频文件 (" + " ".join([f"*{fmt}" for fmt in self.SUPPORTED_INPUT_FORMATS]) + ")"
    
    def get_files_to_convert(self, src_path):
        """获取所有需要转换的文件列表"""
        # 处理带引号的路径
        src_path = src_path.strip('"\'')
        src = P(src_path)
        files = []
        
        if src.is_file():
            if self.is_supported_input(src):
                files = [src]
            else:
                self.log(f"不支持的文件格式: {src}")
        elif src.is_dir():
            # 遍历目录中的所有文件
            for f in src.glob('*'):
                if f.is_file() and self.is_supported_input(f):
                    files.append(f)
            self.log(f"在目录中找到 {len(files)} 个可转换文件")
        else:
            self.log(f"无效的路径: {src_path}")
            
        return files
    
    def get_output_path(self, input_file, output_dir, output_format, overwrite=False):
        """获取输出文件路径，支持覆盖已存在文件"""
        input_path = P(input_file)
        output_dir_path = P(output_dir) if output_dir else input_path.parent
        
        # 确保输出目录存在
        output_dir_path.mkdir(parents=True, exist_ok=True)
        
        # 构建基本输出文件名
        base_name = input_path.stem
        output_ext = output_format if output_format.startswith('.') else f'.{output_format}'
        output_path = output_dir_path / f"{base_name}{output_ext}"
        
        # 如果不覆盖且文件已存在，添加数字后缀
        if not overwrite and output_path.exists():
            counter = 1
            while output_path.exists():
                output_path = output_dir_path / f"{base_name}_{counter}{output_ext}"
                counter += 1
            
        return output_path
    
    def get_audio_codec(self, input_file):
        """获取输入文件的音频编码格式"""
        try:
            cmd = f'ffprobe -v error -select_streams a:0 -show_entries stream=codec_name -of csv=p=0 "{input_file}"'
            result = sp.run(cmd, shell=True, capture_output=True, text=True)
            codec = result.stdout.strip().lower()
            return codec
        except Exception as e:
            self.log(f"获取音频编码时出错: {str(e)}")
            return None
    
    def get_quality_command(self, input_file, output_format, quality, custom_quality=None):
        """获取质量相关的FFmpeg命令参数，支持自定义质量"""
        # 去除格式中的点（如果有的话）
        fmt = output_format.lstrip('.')
        
        # 如果是自定义质量
        if quality == "自定义" and custom_quality:
            # 自动添加k单位
            return f'-b:a {custom_quality}k'
        
        # 获取质量值
        q_val = self.q_map.get(fmt, {}).get(quality, quality)
        
        # 如果需要使用源文件的比特率
        if 'source' in str(q_val):
            try:
                # 使用ffprobe获取源文件的比特率
                cmd = f'ffprobe -v error -select_streams a:0 -show_entries stream=bit_rate -of csv=p=0 "{input_file}"'
                result = sp.run(cmd, shell=True, capture_output=True, text=True)
                br = result.stdout.strip()
                
                if br and br.isdigit():
                    return f'-b:a {int(br)//1000}k'
                else:
                    self.log(f"无法获取源文件比特率，使用默认高质量设置")
                    return self.q_map[fmt]['高质量']
            except Exception as e:
                self.log(f"获取比特率时出错: {str(e)}，使用默认高质量设置")
                return self.q_map[fmt]['高质量']
        
        return q_val
    
    def convert_file(self, input_file, output_format, quality, output_dir=None, custom_quality=None, overwrite=False):
        """转换单个文件，支持自定义质量参数和覆盖文件"""
        try:
            # 获取输出路径
            output_path = self.get_output_path(input_file, output_dir, output_format, overwrite)
            
            # 如果需要覆盖文件，记录日志
            if overwrite and output_path.exists():
                self.log(f"将覆盖已存在文件: {output_path.name}")
            
            # 检查是否满足直接复制AAC流的条件
            copy_aac = False
            if output_format.lower() == '.m4a' and quality == '源文件质量':
                audio_codec = self.get_audio_codec(input_file)
                if audio_codec and audio_codec.startswith('aac'):
                    copy_aac = True
                    self.log(f"检测到AAC音频流，直接复制到M4A容器")
            
            # 构建FFmpeg命令
            if copy_aac:
                # 直接复制音频流，不重新编码
                cmd = f'ffmpeg -i "{input_file}" -vn -y -loglevel error -c:a copy "{output_path}"'
            else:
                # 获取质量参数并构建常规转换命令
                q_cmd = self.get_quality_command(input_file, output_format, quality, custom_quality)
                cmd = f'ffmpeg -i "{input_file}" -vn -y -loglevel error {q_cmd} "{output_path}"'
            
            # 执行转换命令
            sp.run(cmd, shell=True, check=True)
            
            self.log(f"转换成功: {P(input_file).name} → {output_path.name}")
            return True, output_path
        
        except sp.CalledProcessError as e:
            self.log(f"转换失败 (FFmpeg错误): {P(input_file).name} - 错误: {str(e)}")
        except Exception as e:
            self.log(f"转换失败: {P(input_file).name} - 错误: {str(e)}")
        
        return False, None
    
    def batch_convert(self, src_path, output_format, quality, output_dir=None, custom_quality=None, overwrite=False):
        """批量转换文件或目录，支持自定义质量参数和覆盖文件"""
        # 获取需要转换的文件列表
        files = self.get_files_to_convert(src_path)
        total_files = len(files)
        
        if total_files == 0:
            self.log("没有找到可转换的文件")
            return 0, 0
        
        self.log(f"开始转换，共 {total_files} 个文件")
        success_count = 0
        
        # 逐个转换文件
        for i, file in enumerate(files, 1):
            success, _ = self.convert_file(file, output_format, quality, output_dir, custom_quality, overwrite)
            if success:
                success_count += 1
            
            # 更新进度
            progress = int((i / total_files) * 100)
            self.update_progress(progress)
        
        self.log(f"转换完成 - 成功: {success_count}/{total_files}")
        self.update_progress(100)  # 确保进度条达到100%
        return success_count, total_files